﻿using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.Refactoring;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.Editor;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using System;

namespace FxFix
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("Please specify the path to a .sln file on the command line");

                Console.Write("Press any key to continue . . . ");
                Console.ReadKey(true);
                return;
            }



            //string solutionName = @"C:\Users\aravindl\Documents\Visual Studio 2010\Projects\Resolver\Resolver.sln";

           string solutionName = args[0];

           

            StringBuilder reportText = new StringBuilder();
           
            Solution solution = new Solution(solutionName);

           
            Stopwatch watch=new Stopwatch();
            watch.Start();
              Parallel.ForEach(solution.AllFiles,file=>
            {
                var astResolver = new CSharpAstResolver(file.Project.Compilation, file.SyntaxTree, file.UnresolvedTypeSystemForFile);
                Parallel.ForEach(file.SyntaxTree.Descendants.OfType<InvocationExpression>(), invocation =>
                    {
                            // Retrieve semantics for the invocation
                            var rr = astResolver.Resolve(invocation) as InvocationResolveResult;
                            if (rr == null)
                            {
                                // Not an invocation resolve result - e.g. could be a UnknownMemberResolveResult instead
                                //continue;
                                return;
                            }

                            if ((rr.Member.FullName == "System.String.Format" && rr.Member.Parameters[0].Type.ToString()!="System.IFormatProvider"))
                            {
                                reportText.Append(String.Format(CultureInfo.InvariantCulture, "{0}: {1} {2}", invocation.GetRegion(), invocation.GetText(), Environment.NewLine));
                                file.IndexOfInvocations.Add(invocation);

                                if (file.IndexOfInvoc.ContainsKey("CA1305A"))
                                    file.IndexOfInvoc["CA1305A"].Add(invocation);
                                else
                                    file.IndexOfInvoc.Add("CA1305A", new System.Collections.Generic.List<InvocationExpression> { invocation });

                                //Console.WriteLine(rr.Member.Parameters.Count);
                            }

                     

                            if ((rr.Member.FullName == "System.Convert.ToString" ||
                                 rr.Member.FullName == "System.Convert.ToInt32" ||
                                 rr.Member.FullName == "System.Convert.ToInt16") && rr.Member.Parameters.Count == 1)
                            {
                                reportText.Append(String.Format(CultureInfo.InvariantCulture, "{0}: {1} {2}", invocation.GetRegion(), invocation.GetText(), Environment.NewLine));
                                file.IndexOfInvocations.Add(invocation);


                                if (file.IndexOfInvoc.ContainsKey("CA1305"))
                                    file.IndexOfInvoc["CA1305"].Add(invocation);
                                else
                                    file.IndexOfInvoc.Add("CA1305", new System.Collections.Generic.List<InvocationExpression> { invocation });
                            }

                            if ((rr.Member.FullName == "System.Int32.ToString" 
                                || rr.Member.FullName =="System.Int16.ToString") 
                                                            && rr.Member.Parameters.Count == 0)
                            {

                                reportText.Append(String.Format(CultureInfo.InvariantCulture, "{0}: {1} {2}", invocation.GetRegion(), invocation.GetText(), Environment.NewLine));
                                file.IndexOfInvocations.Add(invocation);

                                if (file.IndexOfInvoc.ContainsKey("CA1305I"))
                                    file.IndexOfInvoc["CA1305I"].Add(invocation);
                                else
                                    file.IndexOfInvoc.Add("CA1305I", new System.Collections.Generic.List<InvocationExpression> { invocation });
                            }
                        

                    });
            });


            watch.Stop();
          
            Console.WriteLine(reportText.ToString());
            Console.WriteLine("Found {0} places to refactor in {1} files.",
                          solution.AllFiles.Sum(f => f.IndexOfInvocations.Count),
                          solution.AllFiles.Count(f => f.IndexOfInvocations.Count > 0));
            Console.WriteLine(watch.ElapsedMilliseconds);
            Console.ReadLine();



            Parallel.ForEach(solution.AllFiles, file =>
            {
                if (file.IndexOfInvocations.Count == 0)
                    return;
                // DocumentScript expects the the AST to stay unmodified (so that it fits
                // to the document state at the time of the DocumentScript constructor call),
                // so we call Freeze() to prevent accidental modifications (e.g. forgetting a Clone() call).
                file.SyntaxTree.Freeze();
                // AST resolver used to find context for System.StringComparison generation
                var compilation = file.Project.Compilation;
                var astResolver = new CSharpAstResolver(compilation, file.SyntaxTree, file.UnresolvedTypeSystemForFile);

                // Create a document containing the file content:
                var document = new StringBuilderDocument(file.OriginalText);
                var formattingOptions = FormattingOptionsFactory.CreateAllman();
                var options = new TextEditorOptions();
                using (var script = new DocumentScript(document, formattingOptions, options))
                {
                    Parallel.ForEach(file.IndexOfInvoc, ruleNumber =>
                    {
                        foreach (var invocationAsValue in ruleNumber.Value as System.Collections.Generic.List<InvocationExpression>)
                        {
                            var astBuilder = new TypeSystemAstBuilder(astResolver.GetResolverStateBefore(invocationAsValue));

                            if (ruleNumber.Key == "CA1305" || ruleNumber.Key == "CA1305I")
                            {
                                IType cultureInfo = compilation.FindType(typeof(System.Globalization.CultureInfo));
                                AstType cultureInfoAst = astBuilder.ConvertType(cultureInfo);
                                var copy = (InvocationExpression)invocationAsValue.Clone();
                                copy.Arguments.Add(cultureInfoAst.Member("InvariantCulture"));
                                script.Replace(invocationAsValue, copy);
                            }

                            if (ruleNumber.Key == "CA1305A")
                            {

                                IType cultureInfo = compilation.FindType(typeof(System.Globalization.CultureInfo));
                                AstType cultureInfoAst = astBuilder.ConvertType(cultureInfo);
                                var copy = (InvocationExpression)invocationAsValue.Clone();
                              
                                //copy.Arguments.Add(cultureInfoAst.Member("InvariantCulture"));
                                //script.Replace(invocationAsValue, copy);
                                
                                //invocationAsValue.
                                int offset = script.GetCurrentOffset(invocationAsValue.LParToken.StartLocation)+1;
                                script.InsertText(offset, cultureInfoAst.Member("InvariantCulture").GetText() + ",");

                            }

                        }
                    });

                }
                //File.WriteAllText(Path.ChangeExtension(file.FileName, ".output.cs"), document.Text);
                File.WriteAllText(file.FileName, document.Text);
            });



        }
    }
}
